package com.atguigu.tingshu.order.service.impl;
import java.util.Date;

import cn.hutool.core.date.DateUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.delay.DelayMsgService;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import java.math.RoundingMode;
import java.util.*;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.TingShuException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.google.common.collect.Lists;

import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;


    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private DelayMsgService delayMsgService;

    /**
     * 订单结算页面数据汇总（VIP会员、专辑、声音）
     *
     * @param tradeVo
     * @return 订单信息VO对象
     */
    @Override
    public OrderInfoVo tradeOrderData(Long userId, TradeVo tradeVo) {
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        String itemType = tradeVo.getItemType();
        //支付方式
        orderInfoVo.setPayWay(SystemConstant.ORDER_PAY_WAY_WEIXIN);
        //订单类型
        orderInfoVo.setItemType(itemType);
        if(itemType.equals(SystemConstant.ORDER_ITEM_TYPE_VIP)){
            //vip
            handleVip(tradeVo.getItemId(),orderInfoVo);

        }else if(itemType.equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)){

            //专辑
            handleAlbum(userId,tradeVo,orderInfoVo);

        }else{
            //声音
            handleTrack(userId,tradeVo,orderInfoVo);
        }
        //订单号,防止订单重复提交
        String tradeNo = IdUtil.fastUUID();
        //key
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //保存到redis
        redisTemplate.opsForValue().set(tradeKey,tradeNo,RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        stringObjectMap.remove("payWay");
        //生成签名，防止订单被篡改
        String sign = SignHelper.getSign(stringObjectMap);

        orderInfoVo.setSign(sign);
        return orderInfoVo;
    }

    /**
     * 处理声音
     * @param userId
     * @param tradeVo
     * @param orderInfoVo
     */
    private void handleTrack(Long userId, TradeVo tradeVo, OrderInfoVo orderInfoVo) {
        //获取购买的声音列表
        Result<List<TrackInfo>> waitBuyTrackInfoList = albumFeignClient.getWaitBuyTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
        Assert.notNull(waitBuyTrackInfoList,"获取购买的声音列表异常，声音id：{}",tradeVo.getItemId());
        List<TrackInfo> trackInfoList = waitBuyTrackInfoList.getData();
        Long albumId = trackInfoList.get(0).getAlbumId();
        Result<AlbumInfo> albumInfoResult = albumFeignClient.getAlbumInfo(albumId);
        Assert.notNull(albumInfoResult,"获取专辑信息异常，专辑id：{}",albumId);
        AlbumInfo albumInfo = albumInfoResult.getData();
        //声音没有优惠
        BigDecimal price = albumInfo.getPrice();
        BigDecimal originAmount = price.multiply(new BigDecimal(trackInfoList.size()));
        BigDecimal orderAmount = originAmount;
        BigDecimal derateAmount = BigDecimal.ZERO;
        List<OrderDetailVo> orderDetailVos = trackInfoList.stream().map(trackInfo -> {
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(trackInfo.getId());
            orderDetailVo.setItemName(trackInfo.getTrackTitle());
            orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
            orderDetailVo.setItemPrice(price);
            return orderDetailVo;
        }).collect(Collectors.toList());
        //保存数据
        orderInfoVo.setOriginalAmount(originAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVos);
        orderInfoVo.setOrderDerateVoList(Lists.newArrayList());
    }

    /**
     * 处理专辑
     * @param userId
     * @param tradeVo
     * @param orderInfoVo
     */
    private void handleAlbum(Long userId, TradeVo tradeVo, OrderInfoVo orderInfoVo) {
        Result<Boolean> paidAlbum = userFeignClient.isPaidAlbum(tradeVo.getItemId());
        Assert.notNull(paidAlbum,"用户是否购买过专辑信息异常，专辑id：{}",tradeVo.getItemId());
        Boolean result = paidAlbum.getData();
        if(result){
            throw new TingShuException(404,"用户已购买过该专辑");
        }
        //查询专辑信息
        Result<AlbumInfo> albumInfoResult = albumFeignClient.getAlbumInfo(tradeVo.getItemId());
        Assert.notNull(paidAlbum,"专辑信息异常，专辑id：{}",tradeVo.getItemId());
        AlbumInfo albumInfo = albumInfoResult.getData();
        //用户信息
        Result<UserInfoVo> userInfoVoResult = userFeignClient.getUserInfoVoById(userId);
        Assert.notNull(paidAlbum,"用户信息异常，用户id：{}",userId);
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        BigDecimal originAmount = albumInfo.getPrice();
        BigDecimal derateAmount = BigDecimal.ZERO;
        BigDecimal orderAmount = albumInfo.getPrice();
        //不是vip 或者 是vip但是已过期了
        if(userInfoVo.getIsVip() != 1 || (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().before(new Date()))){
            //不是vip,并且有折扣
            if(albumInfo.getDiscount().intValue() != -1){
                orderAmount = originAmount.multiply(albumInfo.getDiscount())
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
        }else{
            //是vip
            if(albumInfo.getVipDiscount().intValue() != -1){
                orderAmount = originAmount.multiply(albumInfo.getVipDiscount())
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
        }
        derateAmount = originAmount.subtract(orderAmount);
        //订单明细
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setItemId(tradeVo.getItemId());
        orderDetailVo.setItemName(albumInfo.getAlbumTitle());
        orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
        orderDetailVo.setItemPrice(albumInfo.getPrice());
        ArrayList<OrderDetailVo> orderDetailVos = Lists.newArrayList(orderDetailVo);
        //减免金额
        List<OrderDerateVo> orderDerateVos = Lists.newArrayList();
        //减免金额 > 0
        if(derateAmount.intValue() > 0){
            OrderDerateVo orderDerateVo = new OrderDerateVo();
            orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
            orderDerateVo.setDerateAmount(derateAmount);
            orderDerateVo.setRemarks("专辑限时优惠：" + derateAmount);
            orderDerateVos = Lists.newArrayList(orderDerateVo);
        }
        //保存数据
        orderInfoVo.setOriginalAmount(originAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVos);
        orderInfoVo.setOrderDerateVoList(orderDerateVos);

    }

    /**
     * 处理vip订单明细
     * @param itemId vip明细id
     * @param originalAmount 原始金额
     * @param derateAmount  减免金额
     * @param orderAmount 总金额
     * @param orderDetailVoList 订单明细列表
     * @param orderDerateVoList 订单减免明细列表
     */
    private void handleVip(Long itemId,OrderInfoVo orderInfoVo) {
        //会员
        Result<VipServiceConfig> vipServiceConfigResult = userFeignClient.getVipServiceConfig(itemId);
        Assert.notNull(vipServiceConfigResult,"vip详情信息不能为空，vip套餐id：{}",itemId);
        VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
        //原始金额
        BigDecimal originalAmount = vipServiceConfig.getPrice();
        //折后价
        BigDecimal orderAmount = vipServiceConfig.getDiscountPrice();
        //减免金额
        BigDecimal derateAmount = originalAmount.subtract(orderAmount);
        //订单明细
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setItemId(itemId);
        orderDetailVo.setItemName(vipServiceConfig.getName());
        orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
        orderDetailVo.setItemPrice(vipServiceConfig.getPrice());
        ArrayList<OrderDetailVo> orderDetailVos = Lists.newArrayList(orderDetailVo);
        //减免金额
        OrderDerateVo orderDerateVo = new OrderDerateVo();
        orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
        orderDerateVo.setDerateAmount(derateAmount);
        orderDerateVo.setRemarks("vip限时优惠：" + derateAmount);
        ArrayList<OrderDerateVo> orderDerateVos = Lists.newArrayList(orderDerateVo);
        //保存数据
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVos);
        orderInfoVo.setOrderDerateVoList(orderDerateVos);
    }

    /**
     * 提交订单
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
        //获取唯一订单号
        String tradeNoKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //lua脚本删除唯一订单号
        String scriptText = "if(redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(scriptText, Boolean.class);
        boolean flag = (boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeNoKey), orderInfoVo.getTradeNo());
        if (!flag) {
            throw new TingShuException(400, "请勿重复提交订单！");
        }
        //对订单数据进行校验
        Map<String, Object> paramsMap = BeanUtil.beanToMap(orderInfoVo);
        paramsMap.remove("payWay");
        //验签
        SignHelper.checkSign(paramsMap);
        //保存订单，订单明细信息
        OrderInfo orderInfo = this.saveOrderInfo(userId,orderInfoVo);
        //支付方式是扣减余额
        if(orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)){
            AccountLockVo accountLockVo = new AccountLockVo();
            accountLockVo.setOrderNo(orderInfo.getOrderNo());
            accountLockVo.setUserId(userId);
            accountLockVo.setAmount(orderInfo.getOrderAmount());
            accountLockVo.setContent(orderInfo.getOrderTitle());
            //扣减用户金额
            Result checkAndDeductResult = accountFeignClient.checkAndDeduct(accountLockVo);
            if(200 != checkAndDeductResult.getCode()){
                throw new TingShuException(ResultCodeEnum.ACCOUNT_LESS);
            }
            //扣减成功，虚拟发货 远程调用用户服务新增购买记录
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfoVo.getItemType());
            List<OrderDetailVo> orderDetailList = orderInfoVo.getOrderDetailVoList();
            if(!CollectionUtils.isEmpty(orderDetailList)){
                List<Long> orderDetailIds = orderDetailList.stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
                userPaidRecordVo.setItemIdList(orderDetailIds);
            }else{
                userPaidRecordVo.setItemIdList(Lists.newArrayList());
            }
            //新增购买记录
            Result savePaidRecordResult = userFeignClient.savePaidRecord(userPaidRecordVo);
            if(200 != savePaidRecordResult.getCode()){
                throw new TingShuException(211, "新增购买记录异常");
            }
            //订单转改为已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            this.updateById(orderInfo);
        }
        Map<String, String> mapResult = new HashMap<>();
        mapResult.put("orderNo", orderInfo.getOrderNo());
        //其他方式支付，发送延迟消息
        delayMsgService.sendDelayMessage(KafkaConstant.QUEUE_ORDER_CANCEL,orderInfo.getOrderNo(),KafkaConstant.DELAY_TIME1);
        return mapResult;
    }

    /**
     * 保存订单，订单明细信息
     * @param userId
     * @param orderInfoVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public OrderInfo saveOrderInfo(Long userId, OrderInfoVo orderInfoVo) {
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName());
        orderInfo.setUserId(userId);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //生成全局唯一订单编号 形式：当日日期+雪花算法
        String orderNo = DateUtil.today().replaceAll("-","") + IdUtil.getSnowflakeNextId();
        orderInfo.setOrderNo(orderNo);
        //保存
        this.save(orderInfo);
        //保存订单明细
        orderDetailVoList.stream().forEach(orderDetailVo -> {
            OrderDetail orderDetail = new OrderDetail();
            BeanUtil.copyProperties(orderDetailVo, orderDetail);
            orderDetail.setOrderId(orderInfo.getId());
            orderDetailMapper.insert(orderDetail);
        });
        //订单减免明细
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if(!CollectionUtils.isEmpty(orderDerateVoList)){
            orderDerateVoList.stream().forEach(orderDerateVo -> {
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                orderDerate.setOrderId(orderInfo.getId());
                orderDerateMapper.insert(orderDerate);
            });
        }
        return orderInfo;
    }

    @Override
    public OrderInfo getOrderInfo(Long userId, String orderNo) {
        OrderInfo orderInfo = this.getOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getUserId, userId).eq(OrderInfo::getOrderNo, orderNo));
        if(orderInfo == null){
            return null;
        }
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderInfo.getId()));
        if(!CollectionUtils.isEmpty(orderDetails)){
            orderInfo.setOrderDetailList(orderDetails);
        }
        List<OrderDerate> orderDerates = orderDerateMapper.selectList(new LambdaQueryWrapper<OrderDerate>().eq(OrderDerate::getOrderId, orderInfo.getId()));
        if(!CollectionUtils.isEmpty(orderDerates)){
            orderInfo.setOrderDerateList(orderDerates);
        }
        orderInfo.setPayWayName(this.getPayWayName(orderInfo.getPayWay()));
        orderInfo.setOrderStatusName(this.getOrderStatusName(orderInfo.getOrderStatus()));
        return orderInfo;
    }

    @Override
    public Page<OrderInfo> getUserOrderByPage(Page<OrderInfo> pageInfo, Long userId) {
        return baseMapper.getUserOrderByPage(pageInfo,userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderCancel(String orderNo) {
        OrderInfo orderInfo = this.getOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        if(orderInfo != null && orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)){
            this.lambdaUpdate()
                    .eq(OrderInfo::getOrderNo,orderNo)
                    .eq(OrderInfo::getOrderStatus,SystemConstant.ORDER_STATUS_UNPAID)
                    .set(OrderInfo::getOrderStatus,SystemConstant.ORDER_STATUS_CANCEL)
                    .set(OrderInfo::getUpdateTime,new Date())
                    .update();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderPaySuccess(String orderNo) {
        OrderInfo orderInfo = this.getOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        if(orderInfo == null){
            return;
        }
        //已支付或者取消，不处理
        if(orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID) || orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_CANCEL)){
            throw new TingShuException(400,"订单已取消");
        }
        //扣减成功，虚拟发货 远程调用用户服务新增购买记录
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        OrderInfo orderInfoT = this.getOrderInfo(orderInfo.getUserId(), orderNo);
        List<OrderDetail> orderDetailList = orderInfoT.getOrderDetailList();
        if(!CollectionUtils.isEmpty(orderDetailList)){
            List<Long> orderDetailIds = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(orderDetailIds);
        }else{
            userPaidRecordVo.setItemIdList(Lists.newArrayList());
        }
        //新增购买记录
        Result savePaidRecordResult = userFeignClient.savePaidRecord(userPaidRecordVo);
        if(200 != savePaidRecordResult.getCode()){
            throw new TingShuException(211, "新增购买记录异常");
        }
        orderInfo.setUpdateTime(new Date());
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        this.updateById(orderInfo);
    }

    /**
     * 根据支付方式编号得到支付名称
     *
     * @param payWay
     * @return
     */
    private String getPayWayName(String payWay) {
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
            return "微信";
        } else if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)) {
            return "余额";
        } else if (SystemConstant.ORDER_PAY_WAY_ALIPAY.equals(payWay)) {
            return "支付宝";
        }
        return "";
    }

    private String getOrderStatusName(String orderStatus) {
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderStatus)) {
            return "未支付";
        } else if (SystemConstant.ORDER_STATUS_PAID.equals(orderStatus)) {
            return "已支付";
        } else if (SystemConstant.ORDER_STATUS_CANCEL.equals(orderStatus)) {
            return "取消";
        }
        return null;
    }

}
